package tx.os3.clientserver.client.gui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import tx.os3.clientserver.client.handlers.UICommandHandler;
import tx.os3.clientserver.client.model.FileListModel;
import tx.os3.clientserver.client.model.FileRepresentation;

public class FileListComponent extends JComponent implements Observer {

    private static final Icon FILE_ICON = new ImageIcon(
            "resources/file_icon.gif");
    private static final Icon DIR_ICON = new ImageIcon("resources/icon_dir.gif");
    private static final Icon LOADING_ICON = new ImageIcon(
            "resources/loading.gif");

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    // Layout
    private GroupLayout layout;

    // Buttons
    private JButton parentDirButton;
    private JButton deleteButton;
    private JButton renameButton;
    private JButton syncButton;
    private final String parentDirButtonText = "\u21B0";
    private final String deleteButtonText = "Delete";
    private final String renameButtonText = "Rename";
    private final String syncButtonText = "Sync";

    // Labels
    private JLabel loadingLabel;
    private JLabel currentPathLabel;

    // Info panel
    private JPanel infoPanel;
    private JLabel infoFileName;
    private JLabel infoFileSize;
    private JLabel infoDateModified;

    // List component
    private JScrollPane scrollPane;
    private JList fileList;

    // FileListModel
    private FileListModel fileListModel;

    // Command handler
    private UICommandHandler handler;

    public FileListComponent(FileListModel fileListModel,
            UICommandHandler handler) {
        if(handler == null) {
            throw new IllegalArgumentException(
                    "UICommandHandler cannot be null.");
        }
        this.handler = handler;        
        this.layout = new GroupLayout(this);
        this.parentDirButton = new JButton(this.parentDirButtonText);
        this.deleteButton = new JButton(this.deleteButtonText);
        this.renameButton = new JButton(this.renameButtonText);
        this.syncButton = new JButton(this.syncButtonText);
        this.currentPathLabel = new JLabel();
        this.fileList = new JList(new DefaultListModel());
        this.infoPanel = new JPanel();
        this.infoFileName = new JLabel();
        this.infoFileSize = new JLabel();
        this.infoDateModified = new JLabel();
        this.scrollPane = new JScrollPane();

        this.loadingLabel = new JLabel(LOADING_ICON);
        this.fileList.setCellRenderer(new FileListCellRenderer());
        // Set several panel backgrounds to transparent
        this.infoPanel.setBackground(null);

        this.prepareLayoutComponents();
        this.layoutComponents();
        
        this.setFileModel(fileListModel);
    }

    private void prepareLayoutComponents() {
        this.parentDirButton.setEnabled(false);
        this.parentDirButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                handler.handleDirCommand(fileListModel.getParentDir());
                fileListModel.setCurrentDir(fileListModel.getParentDir());
                if(fileListModel.getParentDir() == null
                        || fileListModel.getParentDir().isEmpty()) {
                    fileListModel.setParentDir(FileListModel.ROOT);
                }
                else {
                    int lastSlashIndex = fileListModel.getParentDir()
                            .lastIndexOf("/");
                    String parentDir = fileListModel.getParentDir().substring(
                            0, lastSlashIndex);
                    fileListModel.setParentDir(parentDir);
                }
            }
        });

        // Add a listener for mouse clicks
        this.fileList.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent evt) {
                FileRepresentation selectedFile = (FileRepresentation) fileList
                        .getSelectedValue();
                if(selectedFile != null) {
                    infoFileName.setText(selectedFile.getFileName());
                    if(selectedFile.getLastModified() == null) {
                        infoDateModified.setText("");
                    } else {
                        infoDateModified.setText("Date modified: "
                                + selectedFile.getLastModified());
                    }

                    // Handle double click
                    if(evt.getClickCount() == 2) { // Double-click
                        if(selectedFile.isDirectory()) {
                            int lastSlashIndex = selectedFile.getFullPath()
                                    .lastIndexOf("/");
                            String parentDir = selectedFile.getFullPath()
                                    .substring(0, lastSlashIndex);
                            fileListModel.setParentDir(parentDir);
                            fileListModel.setCurrentDir(selectedFile
                                    .getFullPath());
                            handler.handleDirCommand(selectedFile.getFullPath());
                            parentDirButton.setEnabled(true);
                        }
                        else {
                            // Download file
                            handler.handleGetCommand(selectedFile.getPath()
                                    + selectedFile.getFileName());
                        }
                    }

                }

                deleteButton.setEnabled(selectedFile != null);
                renameButton.setEnabled(selectedFile != null);       
            }
        });

        // Add action listener to delete button
        this.deleteButton.setEnabled(false);
        this.deleteButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(fileList.getSelectedValue() != null) {
                    FileRepresentation file = (FileRepresentation) fileList
                            .getSelectedValue();
                    handler.handleDeleteCommand(file.getFileName());
                    // Refresh file list
                    handler.handleDirCommand(fileListModel.getCurrentDir());
                }
            }
        });

        // Add action listener to rename button
        this.renameButton.setEnabled(false);
        this.renameButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(fileList.getSelectedValue() != null) {
                    FileRepresentation file = (FileRepresentation) fileList
                            .getSelectedValue();

                        String newName = (String) JOptionPane.showInputDialog(
                                FileListComponent.this,
                                "Please enter a new file name.", "Rename",
                                JOptionPane.QUESTION_MESSAGE, null, null,
                                file.getFileName());
                        if(newName == null) {
                            return;
                        }

                        if(newName.isEmpty()) {
                            JOptionPane.showMessageDialog(
                                    FileListComponent.this,
                                    "Given file name is invalid.");
                        }
                        else {
                            handler.handleRenameCommand(file.getFileName(),
                                    newName);
                            // Refresh file list
                            handler.handleDirCommand(fileListModel
                                    .getCurrentDir());
                        }

                }

            }
        });

        this.syncButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                handler.handleSyncCommand();
            }
        });
    }

    private void layoutComponents() {
        // Scrollpane for file list
        this.scrollPane.getViewport().add(this.fileList);

        // Create info panel
        this.infoPanel.setPreferredSize(new Dimension((int) this.infoPanel
                .getSize().getWidth(), 50));
        BoxLayout infoPanelLayout = new BoxLayout(this.infoPanel,
                BoxLayout.Y_AXIS);
        this.infoPanel.setLayout(infoPanelLayout);
        this.infoPanel.add(this.infoFileName);
        this.infoPanel.add(this.infoFileSize);
        this.infoPanel.add(this.infoDateModified);

        this.loadingLabel.setVisible(false);

        // Layout components
        this.setLayout(this.layout);
        this.layout.setHorizontalGroup(this.layout
                .createParallelGroup()
                .addGroup(
                        this.layout.createSequentialGroup()
                        .addComponent(this.parentDirButton)
                        .addComponent(this.loadingLabel)
                        .addComponent(this.currentPathLabel)
                        .addComponent(this.deleteButton)
                        .addComponent(this.renameButton)
                        .addComponent(this.syncButton))
                        .addComponent(this.scrollPane, GroupLayout.DEFAULT_SIZE,
                                GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(this.infoPanel));
        this.layout.setVerticalGroup(this.layout
                .createSequentialGroup()
                .addGroup(
                        this.layout.createParallelGroup()
                        .addComponent(this.parentDirButton)
                        .addComponent(this.loadingLabel)
                        .addComponent(this.currentPathLabel)
                        .addComponent(this.deleteButton)
                        .addComponent(this.renameButton)
                        .addComponent(this.syncButton))
                        .addComponent(this.scrollPane, GroupLayout.DEFAULT_SIZE,
                                GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                .addComponent(this.infoPanel, GroupLayout.PREFERRED_SIZE,
                                        GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE));
    }

    /**
     * Sets the current model for this GUI component.
     * 
     * @param fileListModel
     *            The model to set, cannot be null.
     */
    public void setFileModel(FileListModel fileListModel) {
        if(fileListModel == null) {
            throw new IllegalArgumentException("FileLister cannot be null.");
        }
        this.fileListModel = fileListModel;
        this.fileListModel.addObserver(this);
        // Call command to show directory
        this.handler.handleDirCommand(FileListModel.ROOT);
        this.fileListModel.setCurrentDir(FileListModel.ROOT);
    }

    @Override
    public void update(Observable o, Object arg) {
        if(o != null && o.equals(this.fileListModel)) {
            List<FileRepresentation> files = this.fileListModel.getFiles();

            // Update list of files
            this.fileList.setListData(files.toArray());

            this.parentDirButton.setEnabled(!this.fileListModel.getCurrentDir()
                    .equals(FileListModel.ROOT));
        }
    }

    /**
     * Custom list cell renderer for displaying {@link FileRepresentation}
     * objects.
     * 
     * @author Thomas Middeldorp
     */
    @SuppressWarnings("serial")
    private static class FileListCellRenderer extends DefaultListCellRenderer {

        public FileListCellRenderer() {
            this.setOpaque(true);
        }

        @Override
        public Component getListCellRendererComponent(JList list, Object value,
                int index, boolean isSelected, boolean cellHasFocus) {
            if(value instanceof FileRepresentation) {
                this.setText(((FileRepresentation) value).getFileName());
                if(((FileRepresentation) value).isDirectory()) {
                    this.setIcon(DIR_ICON);
                }
                else {
                    this.setIcon(FILE_ICON);
                }
            }

            if(isSelected) {
                this.setBackground(new Color(255, 215, 122));
            }
            else {
                this.setBackground(Color.WHITE);
            }

            return this;
        }

    }
}
